home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Utilities / Stuart / slog / slog.m < prev    next >
Text File  |  1992-08-10  |  9KB  |  359 lines

  1. /* Replacement for StuartLog in Stuart2.4.
  2.  *
  3.  * This program is in the public domain.  Use and abuse it as you see fit.
  4.  *
  5.  * This program requires setuid-root to run correctly.  There's
  6.  * now a Makefile that sets all of that up - so use make to compile
  7.  * this.  There will be warnings under NeXSTEP3.0.  Of course,
  8.  * consult Stuart's online documentation under Installation/slot
  9.  * for more information.
  10.  *
  11.  *
  12.  * The StuartLog tool in Stuart2.3 was a good idea, but it didn't
  13.  * quite work.  I found that it would often simply hang during the
  14.  * login process, thus either hanging Stuart or not getting the
  15.  * logging functions done.  slog takes a new approach.  It runs
  16.  * continuously from the time Stuart is launched, and the same process
  17.  * handles all logging functions.  Stuart communicates with slog
  18.  * via a private Speaker/Listener pair.  slog also monitors the
  19.  * parent Stuart process and exits on abnormal termination.
  20.  *
  21.  * Admire the code to connect to the parent process.  I think it
  22.  * cost me a kidney.
  23.  *
  24.  * scott hess
  25.  * shess@ssesco.com
  26.  */
  27. #import "SLogListener.h"
  28. #import <objc/HashTable.h>
  29. #import <libc.h>
  30. #import <grp.h>
  31. #import <lastlog.h>
  32. #import <utmp.h>
  33. #import <ttyent.h>
  34. #import <pwd.h>
  35. #import <mach.h>
  36. #import <mach_error.h>
  37. #import <dpsclient/dpsclient.h>
  38. #import <sys/notify.h>
  39.  
  40. @interface SLogger : Object
  41. {
  42.     SLogListener *listener;
  43.     HashTable *slots;
  44.     int uid;
  45.     const char *name;
  46.     port_t parentNotify;
  47. }
  48. - run;
  49. @end
  50. /* Locking open and close.  Though flock() is not a good general-purpose
  51.  * file locker due to NFS limitations.  It works well for this case
  52.  * since only the local machine can access the devices.
  53.  */
  54. int lopen( const char *filename, int openFlags)
  55. {
  56.     int fd=open( filename, openFlags);
  57.     flock( fd, LOCK_EX);
  58.     return fd;
  59. }
  60. int lclose( int fd)
  61. {
  62.     flock( fd, LOCK_UN);
  63.     return close( fd);
  64. }
  65. /* Fix ownerships and permissions on the named pty line. */
  66. void fixOwnership( const char *pty, int uid, int gid, int mod)
  67. {
  68.     char dev[ 64];
  69.     sprintf( dev, "/dev/%s", pty);
  70.     chown( dev, uid, gid);
  71.     chmod( dev, mod);
  72. }
  73. /* Write an entry to wtmp. */
  74. void writeWtmp( struct utmp *ut)
  75. {
  76.     int f=lopen( "/usr/adm/wtmp", O_WRONLY | O_APPEND);
  77.     if( f>=0) {
  78.     write( f, ut, sizeof( struct utmp));
  79.     lclose( f);
  80.     } else {
  81.     perror( "opening /usr/adm/wtmp");
  82.     }
  83. }
  84. /* Write an entry to utmp. */
  85. void writeUtmp( struct utmp *ut, int slot)
  86. {
  87.     if( slot>-1) {
  88.     int f=lopen( "/etc/utmp", O_WRONLY);
  89.     if( f>=0) {
  90.         lseek( f, slot*sizeof( struct utmp), L_SET);
  91.         write( f, ut, sizeof( struct utmp));
  92.         lclose( f);
  93.     } else {
  94.         perror( "opening /etc/utmp");
  95.     }
  96.     }
  97. }
  98.  
  99. @implementation SLogger
  100. /* Initialize uid to an invalid user id (0 is valid). */
  101. - init
  102. {
  103.     self=[super init];
  104.     if( self) {
  105.     uid=-1;
  106.     }
  107.     return self;
  108. }
  109. /* Find the slot in the /etc/ttys file for the given device.  Cache
  110.  * a mapping from the device name to the slot number for future use.
  111.  */
  112. -(int)getSlot:(const char *)device
  113. {
  114.     if( ![slots isKey:device]) {
  115.     struct ttyent *t;
  116.     int slot;
  117.     
  118.     setttyent();
  119.     for( slot=1; t=getttyent(); slot++) {
  120.         if( !strcmp( device, t->ty_name)) {
  121.         break;
  122.         }
  123.     }
  124.     endttyent();
  125.     if( !t) {
  126.         slot=-1;
  127.     }
  128.     if( !slots) {
  129.         slots=[HashTable allocFromZone:[self zone]];
  130.         slots=[slots initKeyDesc:"*" valueDesc:"i" capacity:0];
  131.     }
  132.     device=NXUniqueString( device);
  133.     [slots insertKey:device value:(void *)slot];
  134.     return slot;
  135.     } else {
  136.     return (int)[slots valueForKey:device];
  137.     }
  138. }
  139. /* Login a use on the given pty. */
  140. -(int)login:(char *)pty ownerships:(int)ownership
  141.        utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog
  142. {
  143.     /* Cache a passwd entry for the user if needed. */
  144.     if( uid==-1) {
  145.     /* Grab a passwd entry, set up uid.  This code was suggested
  146.      * by der Mouse <mouse@larry.mcrcim.mcgill.edu>
  147.      */
  148.     char *user=getenv( "USER");
  149.     struct passwd *pw=NULL;
  150.     uid=getuid();
  151.     if( user) {
  152.         pw=getpwnam( user);
  153.     }
  154.     if( !pw || (uid && (uid!=pw->pw_uid))) {
  155.         pw=getpwuid( uid);
  156.     }
  157.     if( pw) {
  158.         uid=pw->pw_uid;
  159.     }
  160.     if( pw) {
  161.         name=NXUniqueString( pw->pw_name);
  162.     } else {
  163.         name="Unknown";
  164.     }
  165.     }
  166.     if( utmp || wtmp || lastlog) {
  167.     struct utmp ut;
  168.  
  169.     /* Clean up the utmp entry. */
  170.     bzero( &ut, sizeof( ut));
  171.  
  172.     /* Set up the ut_name field if necessary. */
  173.     if( wtmp || utmp) {
  174.         strncpy( ut.ut_name, name, sizeof( ut.ut_name));
  175.     }
  176.  
  177.     /* Setup the line and time. */
  178.     strncpy( ut.ut_line, pty, sizeof( ut.ut_line));
  179.     time( &( ut.ut_time));
  180.  
  181.     /* Log to lastlog as needed. */
  182.     if( lastlog) {
  183.         int f=lopen( "/usr/adm/lastlog", O_WRONLY);
  184.         if( f>=0) {
  185.         struct lastlog llog;
  186.         bzero( &llog, sizeof( llog));
  187.         llog.ll_time=ut.ut_time;
  188.         strncpy( llog.ll_line, ut.ut_line, sizeof( llog.ll_line));
  189.         lseek( f, uid*sizeof( llog), L_SET);
  190.         write( f, &llog, sizeof( llog));
  191.         lclose( f);
  192.         } else {
  193.             perror( "opening /usr/adm/lastlog");
  194.         }
  195.     }
  196.     
  197.     /* Log to utmp and wtmp as needed. */
  198.     if( utmp) {
  199.         writeUtmp( &ut, [self getSlot:pty]);
  200.     }
  201.     if( wtmp) {
  202.         writeWtmp( &ut);
  203.     }
  204.     }
  205.     
  206.     /* If needed, set pty ownership to the new user, with permissions
  207.      * set for owner read/write, group write.  Group ownership set
  208.      * to the tty group, if available.
  209.      */
  210.     if( ownership) {
  211.     struct group *gr=getgrnam( "tty");
  212.     fixOwnership( pty, uid, gr ? gr->gr_gid : -1, 0620);
  213.     }
  214.     return 0;
  215. }
  216. -(int)login:(char *)pty ownerships:(int)ownership utmp:(int)utmp
  217. {
  218. #if 0
  219.     return [self login:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp];
  220. #else            /* This may make more sense. */
  221.     return [self login:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES];
  222. #endif
  223. }
  224. -(int)logout:(char *)pty ownerships:(int)ownership
  225.     utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog
  226. {
  227.     if( utmp || wtmp) {
  228.     struct utmp ut;
  229.     
  230.     /* Clean up the utmp entry. */
  231.     bzero( &ut, sizeof( ut));
  232.     
  233.     strncpy( ut.ut_line, pty, sizeof( ut.ut_line));
  234.     time( &( ut.ut_time));
  235.     if( utmp) {
  236.         writeUtmp( &ut, [self getSlot:pty]);
  237.     }
  238.     if( wtmp) {
  239.         writeWtmp( &ut);
  240.     }
  241.     }
  242.  
  243.     /* If needed, set pty ownership back to root user, with permissions
  244.      * set for all read/write.  Group ownership reset to the tty
  245.      * group, if available.
  246.      */
  247.     if( ownership) {
  248.     struct group *gr=getgrnam( "tty");
  249.     fixOwnership( pty, 0, gr ? gr->gr_gid : -1, 0666);
  250.     }
  251.     return 0;
  252. }
  253. -(int)logout:(char *)pty ownerships:(int)ownership utmp:(int)utmp
  254. {
  255. #if 0
  256.     return [self logout:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp];
  257. #else            /* This may make more sense. */
  258.     return [self logout:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES];
  259. #endif
  260. }
  261. /* I use this routine to let Stuart _kindly_ ask slog to exit.  I
  262.  * don't want Stuart doing a kill() on slog while slog's in the middle
  263.  * of something ...
  264.  */
  265. -(void)exit
  266. {
  267.     exit( 0);
  268. }
  269. /* Disconnect our controlling tty and connect to the console device. */
  270. - ttyDisconnect
  271. {
  272.     int tty;
  273.  
  274.     tty=open( "/dev/tty", O_RDWR);
  275.     if( tty>-1) {
  276.     ioctl( tty, TIOCNOTTY, 0);
  277.     close( tty);
  278.     }
  279.     tty=open( "/dev/console", O_WRONLY);
  280.     setpgrp( 0, getpid());
  281.     dup2( tty, 1);
  282.     dup2( tty, 2);
  283.     if( tty!=1 && tty!=2) {
  284.     close( tty);
  285.     }
  286.     return self;
  287. }
  288. /* Catch inadvertant parent process death. */
  289. void notifyPortHandler( notification_t *msg, SLogger *self)
  290. {
  291.     if( msg->notify_header.msg_id==NOTIFY_PORT_DELETED) {
  292.     if( msg->notify_port==self->parentNotify) {
  293.         [self exit];
  294.     }
  295.     }
  296. }
  297. - run
  298. {
  299.     kern_return_t ret;
  300.     msg_header_t initMsg;
  301.     extern int getppid( void);
  302.     task_t parentTask;
  303.     port_t notify;
  304.  
  305.     [self ttyDisconnect];
  306.  
  307.     /* Set up objects for our Listening pleasure. */
  308.     listener=[[SLogListener allocFromZone:[self zone]] init];
  309.     [listener setDelegate:self];
  310.     [listener usePrivatePort];
  311.     [listener addPort];
  312.     /* Give us plenty of leeway for when people logout.
  313.      * Actually, even this isn't really that great,
  314.      * but what can you do?
  315.      */
  316.     port_set_backlog( task_self(), [listener listenPort], PORT_BACKLOG_MAX);
  317.  
  318.     /* Find our parent's notify port. */
  319.     ret=task_by_unix_pid( task_self(), getppid(), &parentTask);
  320.     if( ret!=KERN_SUCCESS) {
  321.     printf( "slog: Unable to get parent's task_t.\n");
  322.     exit( 1);
  323.     }
  324.     ret=task_get_notify_port( parentTask, &parentNotify);
  325.     initMsg.msg_remote_port=parentNotify;
  326.     if( ret!=KERN_SUCCESS) {
  327.     printf( "slog: Unable to get parent's notify port.\n");
  328.     exit( 1);
  329.     }
  330.     port_allocate( task_self(), ¬ify);
  331.     task_set_notify_port( task_self(), notify);
  332.     DPSAddPort( notify, (void *)notifyPortHandler, 64, self, 31);
  333.  
  334.     /* Set up the rest of the header. */
  335.     initMsg.msg_simple=TRUE;
  336.     initMsg.msg_size=sizeof( initMsg);
  337.     initMsg.msg_type=MSG_TYPE_NORMAL;
  338.     initMsg.msg_id=0;
  339.     
  340.     /* Including the port which our Listener listens on. */
  341.     initMsg.msg_local_port=[listener listenPort];
  342.  
  343.     /* Send it, and if successful, enter the event loop. */
  344.     ret=msg_send( &initMsg, SEND_TIMEOUT, 30000);
  345.     if( ret==KERN_SUCCESS) {
  346.     [Listener run];
  347.     }
  348.     printf( "slog: Unable to send Listener port to parent.\n");
  349.     exit( 1);
  350.     return self;
  351. }
  352. @end
  353.  
  354. void main( void)
  355. {
  356.     SLogger *logger=[[SLogger alloc] init];
  357.     [logger run];
  358. }
  359.